package earogya.ehr.patient_details.messages;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.hibernate.HibernateException;

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.DataTypeException;
import ca.uhn.hl7v2.model.v251.datatype.XAD;
import ca.uhn.hl7v2.model.v251.datatype.XPN;
import ca.uhn.hl7v2.model.v251.message.ADR_A19;
import ca.uhn.hl7v2.model.v251.message.ADT_A01;
import ca.uhn.hl7v2.model.v251.segment.AL1;
import ca.uhn.hl7v2.model.v251.segment.MSH;
import ca.uhn.hl7v2.model.v251.segment.PID;
import ca.uhn.hl7v2.model.v251.segment.PD1;
import ca.uhn.hl7v2.model.v251.segment.DB1;
import ca.uhn.hl7v2.model.v251.group.ADR_A19_QUERY_RESPONSE;
import earogya.ehr.patient_details.*;

/***
 * 
 * @author Ureka 
 * This HL7 message is used to Register a patient in the system.
 * 
 */
public class ADT_A04_Handler {
	private ADT_A01 adtMsg;
	Patient patient = new Patient();
	private PatientAddress pAddress;
	private PatientDisability pDisability;
	private PatientEmail pEmail;
	private PatientTelephone pTelephone;
	private PatientAllergy pAllergy;
	private HibernateManager hibernateManager = new HibernateManager();

	public ADT_A04_Handler(ADT_A01 adtMsg) {
		this.adtMsg = adtMsg;
	}

	/**
	 * 
	 * Identify separate segments in the message and add the details in the
	 * segments to their respective POJO classes
	 * 
	 * @return - the acknowledgement of the HL7 message
	 * @throws HL7Exception
	 * @throws IOException
	 */

	public String manageMessage() throws HL7Exception, IOException {
		String ack = null;
		try {
			this.patient.setPatientId(this.enterPatientDetails());
			this.enterPatientAddressDetails();
			this.enterPatientDisabilityDetails();
			this.enterPatientEmailAndTelephoneDetails();
			this.entereAllergyDetails();
			ack = this.createPatientDetailsResponseMessage();
		} catch (HibernateException e) {
			HL7Exception ex = new HL7Exception("Could not save to Database");
			ack = adtMsg.generateACK("AE", ex).encode();
		} catch (IOException e) {
			HL7Exception ex = new HL7Exception("An IO error ocuured");
			ack = adtMsg.generateACK("AE", ex).encode();
		} catch (DataTypeException e) {
			HL7Exception ex = new HL7Exception(
					"An error occured while data type conversion");
			ack = adtMsg.generateACK("AE", ex).encode();
		} catch (HL7Exception e) {
			HL7Exception ex = new HL7Exception("Could not parse the message");
			ack = adtMsg.generateACK("AE", ex).encode();
		} catch (Exception e) {
			HL7Exception ex = new HL7Exception("An error occured");
			ack = adtMsg.generateACK("AE", ex).encode();
		}
		return ack;
	}

	/**
	 * Save the patient's demographic details to the database
	 * 
	 * @return
	 * @throws HL7Exception
	 * @throws IOException
	 * @throws HibernateException
	 * @throws DataTypeException
	 * 
	 */
	public int enterPatientDetails() throws HL7Exception, IOException, HibernateException, DataTypeException {
		PID pid = adtMsg.getPID();
		PD1 pd1 = adtMsg.getPD1();
		XPN pName = pid.getPatientName(0);
		patient.setPrefix(pName.getPrefixEgDR().getValue());
		patient.setGivenName(pName.getGivenName().getValue());
		patient.setFamilyName(pName.getFamilyName().getSurname().getValue());
		patient.setNicNo(pid.getPatientIdentifierList(0).getIDNumber().getValue());
		patient.setGender(pid.getAdministrativeSex().toString());
		if (pid.getDateTimeOfBirth().getTime().getValue() == null) {
		} 
		else {
			patient.setBirthTime(pid.getDateTimeOfBirth().getTime().getValueAsDate());
		}
		patient.setBirthPlaceAddress(pid.getBirthPlace().toString());
		patient.setMultipleBirthIndex((pid.getMultipleBirthIndicator().getValue() == null) 
				? 0 : Short.valueOf(pid.getMultipleBirthIndicator().getValue()));
		patient.setMultipleBirthOrderNumber((pid.getBirthOrder().getValue() == null) 
				? 0 : Short.valueOf(pid.getBirthOrder().getValue()));
		patient.setOrganDonorIndex((pd1.getOrganDonorCode().getValue() == null) 
				? 0 : Integer.valueOf(pd1.getOrganDonorCode().getValue()));
		patient.setMaritialStatus(pid.getMaritalStatus().getText().toString());
		patient.setLivingArrangementId((pd1.getLivingArrangement().getValue() == null) 
				? 0 : Integer.valueOf(pd1.getLivingArrangement().getValue()));
		patient.setEthnicGroupId(pid.getEthnicGroup(0).getText().toString());
		patient.setReligiousAffliationId(pid.getReligion().getText().toString());
		patient.setDeceasedIndex(false);
		patient.setIsActive(true);

		return hibernateManager.saveObject(patient);

	}

	
	/**
	 * Save the patient's address details to the database
	 * 
	 * @throws HL7Exception
	 * @throws IOException
	 * @throws HibernateException
	 */
	private void enterPatientAddressDetails() throws HL7Exception, IOException,
			HibernateException {
		PID pid = adtMsg.getPID();
		XAD[] addressList = pid.getPatientAddress();
		for (int i = 0; i < addressList.length; i++) {
			pAddress = new PatientAddress();
			pAddress.setPatientId(this.patient.getPatientId());
			pAddress.setCity1(addressList[i].getOtherDesignation().getValue());
			pAddress.setCountry(addressList[i].getCountry().getValue());
			pAddress.setHouseNumber(addressList[i].getStreetAddress().getDwellingNumber().getValue());
			pAddress.setCity2(addressList[i].getCity().getValue());
			pAddress.setStreet(addressList[i].getStreetAddress().getStreetName().getValue());
			hibernateManager.saveObject(pAddress);
		}
	}

	
	/**
	 * 
	 * Save the patient's disability details to the database.
	 * 
	 * @throws DataTypeException
	 * @throws IOException
	 * @throws HibernateException
	 * @throws HL7Exception
	 */
	private void enterPatientDisabilityDetails() throws DataTypeException,IOException, HibernateException, HL7Exception {
		DB1 db1;
		int db1Repetitions = adtMsg.getDB1Reps();
		for (int i = 0; i < db1Repetitions; i++) {
			db1 = adtMsg.getDB1(i);
			pDisability = new PatientDisability();
			pDisability.setPatientId(this.patient.getPatientId());
			pDisability.setDisabilityId(Integer.parseInt(db1.getDisabledIndicator().getValue()));
			hibernateManager.saveObject(pDisability);
		}
	}

	
	/**
	 * 
	 * Save the patient's email and address details to the database
	 * 
	 * @throws DataTypeException
	 * @throws IOException
	 * @throws HibernateException
	 * @throws HL7Exception
	 */
	private void enterPatientEmailAndTelephoneDetails() throws DataTypeException, IOException, HibernateException,HL7Exception {
		PID pid = adtMsg.getPID();
		int i = 0;
		while (!(pid.getPhoneNumberHome(i).getTelephoneNumber().getValue() == null)
				|| !(pid.getPhoneNumberHome(i).getEmailAddress().getValue() == null)) {

			if (pid.getPhoneNumberHome(i).getTelephoneNumber().getValue() == null) {
			} 
			else {
				pTelephone = new PatientTelephone();
				pTelephone.setPatientId(this.patient.getPatientId());
				pTelephone.setTelephoneNo(pid.getPhoneNumberHome(i).getTelephoneNumber().getValue());
				hibernateManager.saveObject(pTelephone);
			}
			if (pid.getPhoneNumberHome(i).getEmailAddress().getValue() == null) {
			} 
			else {
				pEmail = new PatientEmail();
				pEmail.setPatientId(this.patient.getPatientId());
				pEmail.setEmail(pid.getPhoneNumberHome(i).getEmailAddress().getValue());
				hibernateManager.saveObject(pEmail);
			}
			i++;
		}

	}

	
	/**
	 * 
	 * Save the patient's allergy details to the database.
	 * 
	 * @throws DataTypeException
	 * @throws IOException
	 * @throws HibernateException
	 * @throws HL7Exception
	 */
	private void entereAllergyDetails() throws DataTypeException, IOException,HibernateException, HL7Exception {
		AL1 al1;
		for (int i = 0; i < adtMsg.getAL1Reps(); i++) {
			al1 = adtMsg.getAL1(i);
			pAllergy = new PatientAllergy();
			pAllergy.setAllergyCode(al1.getAllergenCodeMnemonicDescription().getComponent(0).toString());
			pAllergy.setPatientId(this.patient.getPatientId());
			pAllergy.setSeverity(al1.getAllergySeverityCode().getComponent(0).toString());
			hibernateManager.saveObject(pAllergy);
		}
	}

	
	/**
	 * 
	 * Create the ADRA19 message with the registered patient's details to send to the front-end
	 * 
	 * @return
	 * @throws DataTypeException
	 * @throws IOException
	 * @throws HibernateException
	 * @throws HL7Exception
	 */
	public String createPatientDetailsResponseMessage()
			throws DataTypeException, IOException, HibernateException,
			HL7Exception {
		String msg = null;
		ADR_A19 ADRA19msg = new ADR_A19();
			Object[][] fieldsAndValues = { { "patientId",
					this.patient.getPatientId() } };
			String[] requiredFields = { "patientId", "prefix", "givenName",
					"familyName", "nicNo", "gender", "birthTime",
					"birthPlaceAddress", "multipleBirthIndex",
					"multipleBirthOrderNumber", "organDonorIndex",
					"maritialStatus", "educationalLevelId",
					"livingArrangementId", "religiousAffliationId",
					"ethnicGroupId", "confidentialityLevelId",
					"veryImportantPatientId", "deceasedIndex", "deceasedTime" };

			List patientList = new HibernateManager().queryValueListFromDatabase(fieldsAndValues,requiredFields, "Patient", "patient");
			MSH mshSegment = ADRA19msg.getMSH();
			mshSegment.getFieldSeparator().setValue("|");
			mshSegment.getEncodingCharacters().setValue("^~\\&");
			mshSegment.getDateTimeOfMessage().getTime().setValue(Calendar.getInstance().getTime());
			mshSegment.getMessageType().getMsg1_MessageCode().setValue("ADR");
			mshSegment.getVersionID().getVersionID().setValue("2.5.1");
			mshSegment.getMessageType().getTriggerEvent().setValue("A19");
			for (int i = 0; i < patientList.size(); i++) {
				Object[] patientDetails = (Object[]) patientList.get(i);
				ADR_A19_QUERY_RESPONSE response = ADRA19msg.insertQUERY_RESPONSE(i);
				PID pid = response.getPID();
				pid.getPatientID().getIDNumber().setValue(patientDetails[0].toString());
				if (patientDetails[1] == null) {
				} 
				else {
					pid.getPatientName(0).getPrefixEgDR().setValue(patientDetails[1].toString());
				}
				if (patientDetails[2] == null) {
				} 
				else {
					pid.getPatientName(0).getGivenName().setValue(patientDetails[2].toString());
				}
				if (patientDetails[3] == null) {
				} 
				else {
					pid.getPatientName(0).getFamilyName().getSurname().setValue(patientDetails[3].toString());
				}
				if (patientDetails[4] == null) {
				} 
				else {
					pid.getPatientIdentifierList(0).getIDNumber().setValue(patientDetails[4].toString());
				}
				if (patientDetails[5] == null) {
				} 
				else {
					pid.getAdministrativeSex().setValue(patientDetails[5].toString());
				}

				if (patientDetails[6] == null) {
				} 
				else {
					pid.getDateTimeOfBirth().getTime().setValue((Date) patientDetails[6]);
				}
				if (patientDetails[7] == null) {
				} 
				else {
					pid.getBirthPlace().setValue(patientDetails[7].toString());
				}
				if (patientDetails[11] == null) {
				} 
				else {
					pid.getMaritalStatus().getText().setValue(patientDetails[11].toString());
				}
				if (patientDetails[15] == null) {
				} 
				else {
					pid.getEthnicGroup(0).getText().setValue(patientDetails[15].toString());
				}
				if (patientDetails[14] == null) {
				} 
				else {
					pid.getReligion().getText().setValue(patientDetails[14].toString());
				}
				List patientAddressList = this.getPatientAddressDetails(Integer.parseInt(patientDetails[0].toString()));
				for (int j = 0; j < patientAddressList.size(); j++) {
					Object[] patientAddress = (Object[]) patientAddressList.get(j);
					if (patientAddress[4] == null) {
					} 
					else {
						pid.getPatientAddress(0).getOtherDesignation().setValue(patientAddress[4].toString());
					}
					if (patientAddress[6] == null) {
					} 
					else {
						pid.getPatientAddress(0).getCountry().setValue(patientAddress[6].toString());
					}
					if (patientAddress[2] == null) {
					} 
					else {
						pid.getPatientAddress(0).getStreetAddress().getDwellingNumber().setValue(patientAddress[2].toString());
					}
					if (patientAddress[5] == null) {
					} 
					else {
						pid.getPatientAddress(0).getCity().setValue(patientAddress[5].toString());
					}
					if (patientAddress[3] == null) {
					} 
					else {
						pid.getPatientAddress(0).getStreetAddress().getStreetName().setValue(patientAddress[3].toString());
					}

				}
				List patientEmailList = this.getPatientEmailDetails(Integer.parseInt(patientDetails[0].toString()));
				for (int j = 0; j < patientEmailList.size(); j++) {
					Object[] patientEmail = (Object[]) patientEmailList.get(j);
					pid.getPhoneNumberHome(j).getEmailAddress().setValue(patientEmail[2].toString());
				}

				List patientTelephoneList = this.getPatientTelephoneDetails(Integer.parseInt(patientDetails[0].toString()));
				for (int j = 0; j < patientTelephoneList.size(); j++) {
					Object[] patientTelephone = (Object[]) patientTelephoneList.get(j);
					pid.getPhoneNumberHome(j).getTelephoneNumber().setValue(patientTelephone[2].toString());
				}
				msg = ADRA19msg.getMessage().encode();
			}
		return msg;
	}

	
	/**
	 * 
	 * Get the patient's address details from the database
	 * 
	 * @param patientId
	 * @return
	 * @throws DataTypeException
	 * @throws IOException
	 * @throws HibernateException
	 * @throws HL7Exception
	 */
	private List getPatientAddressDetails(int patientId)throws DataTypeException, IOException, HibernateException,HL7Exception {
		Object[][] fieldsAndValues = { { "patientId", patientId } };
		String[] requiredFields = { "patientAddressId", "patientId",
									"houseNumber", "street", "city1", "city2", "country" };
		List patientAddressList = hibernateManager.queryValueListFromDatabase(fieldsAndValues, requiredFields, "PatientAddress","patientAddress");
		return patientAddressList;
	}

	
	/**
	 * 
	 * Get the patient's email address details from the database
	 * 
	 * @param patientId
	 * @return
	 * @throws DataTypeException
	 * @throws IOException
	 * @throws HibernateException
	 * @throws HL7Exception
	 */
	private List getPatientEmailDetails(int patientId) throws DataTypeException, IOException, HibernateException,HL7Exception {
		Object[][] fieldsAndValues = { { "patientId", patientId } };
		String[] requiredFields = { "patientEmailId", "patientId", "email" };
		List patientEmailList = hibernateManager.queryValueListFromDatabase(fieldsAndValues, requiredFields,"PatientEmail", "patientEmail");
		return patientEmailList;
	}

	
	/**
	 * 
	 * Get the patients telephone details from the database
	 * 
	 * @param patientId
	 * @return
	 * @throws DataTypeException
	 * @throws IOException
	 * @throws HibernateException
	 * @throws HL7Exception
	 */
	private List getPatientTelephoneDetails(int patientId) throws DataTypeException, IOException, HibernateException,HL7Exception {
		Object[][] fieldsAndValues = { { "patientId", patientId } };
		String[] requiredFields = { "patientTelephoneNoId", "patientId","telephoneNo" };
		List patientTelephoneList = hibernateManager.queryValueListFromDatabase(fieldsAndValues, requiredFields,"PatientTelephone", "patientTelephone");
		return patientTelephoneList;
	}

}
